#ifndef __ETLALLOC_H__
#define __ETLALLOC_H__

#ifndef __ETL_H__
	#error etlAlloc.h requires etl.h to be included first
#endif

//namespace ETL_AllocHelper
//{

template<class TYPE>
inline void ConstructObjects(TYPE* pObjects, int nCount)
{
	chASSERT(pObjects != NULL && nCount > 0);

	// first do bit-wise zero initialization
	memset((void*)pObjects, 0, nCount * sizeof(TYPE));

	// then call the constructor(s)
	for (; nCount--; pObjects++)
		::new((void*)pObjects) TYPE;
}

template<class TYPE>
inline void DestructObjects(TYPE* pObjects, int nCount)
{
	chASSERT(pObjects != NULL && nCount > 0);

	// call the destructor(s)
	for (int i = 0; i < nCount; i++)
		(pObjects + i)->~TYPE();
	// first do bit-wise zero initialization
	memset((void*)pObjects, 0, nCount * sizeof(TYPE));
}

template<class TYPE>
inline void CopyObjects(TYPE* pDest, const TYPE* pSrc, int nCount)
{
	chASSERT(pDest != NULL && pSrc != NULL && nCount >= 0);
	
	// default is element-copy using assignment
	if(pSrc < pDest)
	{
		int i = 0;
		for(i = nCount - 1 ; i >= 0; i--)
			pDest[i] = pSrc[i];
	}
	else if(pSrc > pDest)
	{
		int i = 0;
		for(i = 0 ; i < nCount; i++)
			pDest[i] = pSrc[i];
	}
}

template<class TYPE>
inline void MoveObjects(TYPE* pDest, TYPE* pSrc, int nCount)
{
	chASSERT(pDest != NULL && pSrc != NULL && nCount > 0);
	if(pSrc < pDest)
	{
		int i;
		for(i = nCount - 1 ; i >= 0; i--)
		{
			ConstructObjects(pDest+i, 1);
			pDest[i] = pSrc[i];
			DestructObjects(pSrc + i, 1);
		}
	}
	else if(pSrc > pDest)
	{
		int i;
		for(i = 0 ; i < nCount; i++)
		{
			ConstructObjects(pDest+i, 1);
			pDest[i] = pSrc[i];
			DestructObjects(pSrc + i, 1);
		}
	}
}

class chBlock
{
public:
	chBlock(LPVOID pData, DWORD nDataSize)
	{
		m_pData = pData;
		m_nDataSize = nDataSize;
	}
	chBlock()
	{
		m_pData = NULL;
		m_nDataSize = 0;
	}
public:
	LPVOID	m_pData;
	DWORD	m_nDataSize;
};
//////////////////////////////////////////////////////////////////////////
// chObjAlloc

template <class T>
class chObjAlloc
{
protected:
	typedef chObjAlloc<T>		type_chObjAlloc;
public:
	//////////////////////////////////////////////////////////////////////////
	// Alloc & Free
	chObjAlloc& Alloc(int nCount)
	{
		m_aT = (T*)realloc(m_aT, nCount*sizeof(T));
		chASSERT(m_aT != NULL);
		m_nAllocCount = nCount;
		return *this;
	}
	
	void Free()
	{
		if(m_aT != NULL || m_nAllocCount != 0)
		{
			free(m_aT);
			m_aT = NULL;
			m_nAllocCount = 0;
		}
	}
	T* GetData() const
	{ return m_aT; }

//	operator T* () const
//	{ return m_aT; }
private:
	chObjAlloc(chObjAlloc& srcAlloc)
	{
		chASSERT(FALSE);
	}
	chObjAlloc& operator = (chObjAlloc& srcAlloc)
	{
		chASSERT(FALSE);
	}
public:
	// Construction/destruction
	chObjAlloc()
	{
		m_aT = NULL;
		m_nAllocCount = 0;
	}
	
	~chObjAlloc()
	{
		chASSERT(m_aT == NULL && m_nAllocCount == 0);
	}
protected:
	T*	m_aT;
	int	m_nAllocCount;
};

//////////////////////////////////////////////////////////////////////////
// class chHashAlloc
template <class T, int rowCount>
class chHashAlloc
{
	typedef chObjAlloc<T>					type_chObjAlloc;
	struct hash_block
	{
		hash_block*		pNext;
		type_chObjAlloc	objArray;
		hash_block() : pNext(NULL){}
	};
protected:
	T& BorrowObject()
	{
		if(m_pObjHead == NULL)
		{
			type_chObjAlloc& rNodeArray = AllocHashBlock(rowCount);
			T* pNode = rNodeArray.GetData();
			int i = 0;
			for(i = 0; i < rowCount - 1; i++)
				pNode[i].pNext = pNode + i + 1;
			pNode[i].pNext = NULL;

			m_pObjHead = rNodeArray.GetData();
		}
		
		T* pTmpNode = m_pObjHead;
		m_pObjHead = m_pObjHead->pNext;
		m_nBorrowCount ++;

		ConstructObjects<T>(pTmpNode, 1); // construct it
		
		return *pTmpNode;
	}
	void ReturnObject(T& obj)
	{
		chASSERT(m_nBorrowCount > 0);
		DestructObjects<T>(&obj, 1); // destruct it

		obj.pNext = m_pObjHead;
		m_pObjHead = &obj;
		m_nBorrowCount --;
	}
protected:
	type_chObjAlloc& AllocHashBlock(int nSize)
	{
		chASSERT(nSize > 0 && m_pObjHead == NULL);
		hash_block* pNewBlock = new hash_block;
		pNewBlock->pNext = m_pHashHead;
		m_pHashHead = pNewBlock;
		pNewBlock->objArray.Alloc(nSize);
		return pNewBlock->objArray;
	}
	void FreeAll()
	{
		chASSERT(m_nBorrowCount == 0);
		while(m_pHashHead != NULL)
		{
			hash_block* pTmpBlock = m_pHashHead;
			m_pHashHead = m_pHashHead->pNext;
			
			pTmpBlock->objArray.Free();
			delete pTmpBlock;
		}

		m_pHashHead = NULL;
		m_pObjHead = NULL;
		m_nBorrowCount = 0;
	}
protected:
	chHashAlloc()
	{
		m_pHashHead = NULL;
		m_pObjHead = NULL;
		m_nBorrowCount = 0;
	}
	~chHashAlloc()
	{
		chASSERT(m_nBorrowCount == 0);
		FreeAll();
	}
protected:
	int					m_nBorrowCount;
	T*					m_pObjHead;
	hash_block*			m_pHashHead;
};

//////////////////////////////////////////////////////////////////////////
// class hash_node

template <class T>
PRIVATE_CLASS hash_node
{
public:
	hash_node* pNext;
	hash_node* pPrev;
	T vData;
public:
	hash_node() : pNext(NULL), pPrev(NULL){}
};

template <class T>
PRIVATE_CLASS hash_iterator
{
	typedef hash_node<T>		type_hash_node;
public:
	type_hash_node* pThisNode;
	hash_iterator(type_hash_node* pNode = NULL) : pThisNode(pNode)
	{}
	
	bool operator == (const hash_iterator& other) const
	{ return pThisNode == other.pThisNode; }
	bool operator!=(const hash_iterator& other) const
	{ return pThisNode != other.pThisNode; }
	T& operator*() const
	{ return pThisNode->vData; }

	hash_iterator& operator++()
	{
		pThisNode = pThisNode->pNext;
		return *this;
	}
	hash_iterator operator++(int)
	{ 
		hash_iterator tmp = *this;
		pThisNode = pThisNode->pNext;
		return tmp;
	}
	hash_iterator& operator--()
	{ 
		pThisNode = pThisNode->pPrev;
		return *this;
	}
	hash_iterator operator--(int)
	{ 
		hash_iterator tmp = *this;
		pThisNode = pThisNode->pPrev;
		return tmp;
	}
};

//};
//using namespace ETL_AllocHelper;

#endif //__ETLALLOC_H__
